home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-04-28 | 28.7 KB | 1,258 lines | [TEXT/MPS ] |
- #ifndef __CDENT__
- #include "cdent.h"
- #endif
-
- #ifndef __CSCANNER__
- #include "CScanner.h"
- #endif
-
- #ifndef __FORMATLOG__
- #include "FormatLog.h"
- #endif
-
- #ifndef __FORMATTING__
- #include "Formatting.h"
- #endif
-
- #ifndef __PARSERACTIONS__
- #include "ParserActions.h"
- #endif
-
- #ifndef __CTYPE__
- #include <ctype.h>
- #endif
-
- #ifndef __MEMORY__
- #include <memory.h>
- #endif
-
-
- /*
- ** How to squeeze when lines exceed the output line length. The normal case
- ** is kSqueezeNone, which means that data are output exactly as they are seen
- ** without any other editorializing. The first other case is kSqueezeBlanks,
- ** which removes optional blanks. If this still doesn't scrunch the source,
- ** the final mechanism of kSqueezeConditionalNewLine is called into play, which
- ** interprets optional newlines as being mandatory newlines.
- */
- enum {
- kSqueezeNone, // Keep all formatting information
- kSqueezeBlanks, // Remove optional blanks
- kSqueezeUseSourceNewLines, // Use the original lines in the source
- kSqueezeConditionalNewLine, // Use conditional newlines
-
- kSqueezeEnd, // Last value, not used
- kSqueezeFirst = kSqueezeBlanks // First value to try squeezing
- };
-
-
- //ƒ-
- const short kDfltIndentDelta = 4;
- const short kDfltWidth = 0;
- const short kDfltIndent = 0;
- const short kDfltTempIndent = 2;
- const short kDfltTabSize = 4;
- const short kDfltCommentColumn = 48;
- const short kDfltLineLength = 120;
- const Boolean kDfltReformatComments = false;
- const Boolean kDfltSaveSourceNewLines = false;
- const Boolean kDfltPassConsecutiveNewLines = true;
- //ƒ+
-
-
- /*
- ** Static class variables
- */
- //ƒ-
- DFile* Formatting::gOutput = 0;
- FormatLog* Formatting::gFormatLog = 0;
- short Formatting::gTabSize = kDfltTabSize;
- short Formatting::gLineLength = kDfltLineLength;
- short Formatting::gCommentColumn = kDfltCommentColumn;
-
- Boolean Formatting::gDebug = false;
-
- Boolean Formatting::gReformatComments = kDfltReformatComments;
- Boolean Formatting::gPassSourceNewLines = kDfltSaveSourceNewLines;
- Boolean Formatting::gPassConsecutiveNewLines = kDfltPassConsecutiveNewLines;
-
- Boolean Formatting::gFromSource = true;
- short Formatting::gSourceNewLines = 0;
- Boolean Formatting::gLastNewLineFromSource = true;
- //ƒ+
-
-
- //ƒ-
- struct RollbackVars Formatting::gV = {kSLex_NewLine, 0, true};
- struct RollbackVars Formatting::gC;
-
- size_t Formatting::gOutputOffset = 0;
-
- static Boolean gConditionalNeedNewLine = false;
- static FormatString gConditionalNewLineGlue = 0;
- //ƒ+
-
-
- //µ Formatting::IFormatting
- #pragma segment Formatting
- short Formatting::IFormatting()
- {
- short err = fSavedContexts.IDataArea();
-
- if (err != noErr)
- return (err);
-
- SetComma(gFS_expr1);
- SetSemi(gFS_stmt1);
- SetLCurly(gFS_block1);
- SetRCurly(gFS_block2);
- SetLParen(gFS_expr5);
- SetRParen(gFS_expr7);
- SetName(gFS_name1);
- SetOperator(gFS_expr4);
- SetAssign(gFS_expr6);
-
- SetRegister(kRegIndentDelta, kDfltIndentDelta);
- SetRegister(kRegWidth, kDfltWidth);
- SetRegister(kRegIndent, kDfltIndent);
- SetRegister(kRegCurrentColumn, 0);
- SetRegister(kRegTempIndent, kDfltTempIndent);
-
- fContext.fDeclWidth = 0; // No minimum width for declarations
- fContext.fDeclLeft = false; // "*" and "&" go right
- SetWhatToSqueeze(kSqueezeNone); // Don't squeeze anything
-
- // Safety measure. Drop a small context as the first one
- fSavedContexts.Write(&fContext, sizeof(fContext));
- fSavedContexts.DecrCursor(sizeof(fContext));
-
- return (noErr);
- }
-
-
-
- // µ Formatting::IFormatting
- #pragma segment Formatting
- short Formatting::IFormatting(const Formatting *aFormat)
- {
- BlockMove(aFormat, this, sizeof(this));
- return (fSavedContexts.IDataArea(&aFormat->fSavedContexts));
- }
-
-
-
- //µ Formatting::SetFormatLog
- #pragma segment Formatting
- void Formatting::SetFormatLog(FormatLog *aFormatLog)
- {
- gFormatLog = aFormatLog;
- gFormatLog->Checkpoint(&fContext, &fSavedContexts, fSavedContexts.GetIndex(sizeof(fContext)));
- gC = gV;
- gOutputOffset = gOutput->GetCursor();
- }
-
-
-
-
- /*
- ** Make sure we are at the beginning of a new line
- ** Postcondition: LogicalNewLine()
- */
- //µ Formatting::FreshLine
- #pragma segment Formatting
- void Formatting::FreshLine()
- {
- if (!IsBOL())
- NewLine();
- else
- LogicalNewLine();
- }
-
-
-
- /*
- ** Move the cursor to the given column. If the cursor is past the column,
- ** leave the cursor where it is unless whitespace is requested.
- */
- //µ Formatting::IndentTo
- #pragma segment Formatting
- void Formatting::IndentTo(short aColumn)
- {
- if (CurrentColumn() < aColumn) {
- CurrentColumn() -= CurrentColumn() % gTabSize;// Make tabs work right
- while (CurrentColumn() + gTabSize <= aColumn) {
- gOutput->Putc('\t');
- CurrentColumn() += gTabSize;
- }
-
- while (CurrentColumn() < aColumn) {
- gOutput->Putc(' ');
- CurrentColumn()++;
- }
-
- if (!IsBOL())
- LogicalSpace();
- }
- }
-
-
-
- /*
- ** Move the cursor to the indent column on a fresh line.
- */
- //µ Formatting::IndentLine
- #pragma segment Formatting
- void Formatting::IndentLine(short aColumn)
- {
- FreshLine();
- IndentTo(aColumn);
- }
-
-
-
- /*
- ** Format the comment as either an end of line type comment or as a line
- ** or block comment at the current indent level
- */
- //µ Formatting::Comment
- #pragma segment Formatting
- void Formatting::Comment(const char *start, const char *end)
- {
- short commentColumn = (CurrentColumn() == 0) ? gV.fIndent : gCommentColumn;
-
- /* Advance to the comment column */
- IndentTo(commentColumn);
-
- /*
- ** Comments to the end of line are not wrapped, they are just written
- ** as is. The comment will not be terminated by a newline: the newline
- ** appears as the next token
- */
- if (start[1] == '/') {
- _Write(start, end - start);
- NeedNewLine(1);
- return;
- }
-
- /*
- ** The comment is a standard C comment. Advance past the leading comment
- ** start and then display the comment. We save/restore gFromSource around
- ** these calls as the newlines that are desired should not flush buffers.
- */
- Boolean saveFromSource = gFromSource;
- gFromSource = false;
-
- start += 2;
- _Write("/*", 2);
- if (true || !gReformatComments) {
- Boolean twoStar = false; // lines prefaced by "**"
- Boolean oneStar = false; // lines prefaced by " *"
-
- while (start < end) {
- switch (*start) {
- case '\n':
- // Advance to the first non-blank on the line. If there are
- // any intervening newlines, emit them immediately.
- start++;
- while (isspace(*start)) {
- if (*start == '\n')
- NewLine();
- start++;
- }
-
- // Indent to the comment column
- NewLine();
- IndentTo(commentColumn);
-
- // Indent the body of the line. If the line starts with
- // "**", no extra spaces are required. If the line
- // starts with "*", then one extra space is required.
- // If the line is the last line of a comment, then either
- // zero or one extra space is required: zero when the
- // previous lines started with zero or two "*", one when
- // the previous lines began with a single star.
- if (start[0] != '*') {
- oneStar = false;
- twoStar = false;
- _Write(" ", 2);
- } else if (start[1] == '*') {
- oneStar = false;
- twoStar = true;
- } else {
- if (start[1] != '/' || oneStar)
- _Putc(' ');
- oneStar = true;
- twoStar = false;
- }
- _Putc(*start);
- break;
-
- default:
- _Putc(*start);
- break;
- }
-
- start++;
- }
- } else {
- // Reformat the comment. Eventually
- }
-
- // Restore the saved value of gFromSource.
- gFromSource = saveFromSource;
-
- // Indicate that this was a newline
- gV.fLastTokenType = kSLex_Comment;
- }
-
-
-
- /*
- ** Specify the glue to use for the next calls to display. The glue is
- ** immediately interpreted until the "•" character, which requires an item,
- ** is scanned. Because we are "greedy", there is no format left to
- ** consume so that only assignment is necessary.
- */
- //µ Formatting::SetGlue
- #pragma segment Formatting
- void Formatting::SetGlue(FormatString aGlue)
- {
- gFormatLog->Record(Formatting::SetGlue, aGlue);
- gV.fFormatString = aGlue;
- gV.fGlueString = Interpret(aGlue);
- if (gV.fGlueString == 0)
- diag(kFatal, "Bad formatting string %s\n", aGlue);
- }
-
-
-
- /*
- ** Execute the glue without disturbing any other glue which might
- ** be in place.
- */
- //µ Formatting::ExecuteGlue
- #pragma segment Formatting
- void Formatting::ExecuteGlue(FormatString aGlue)
- {
- gFormatLog->Record(Formatting::ExecuteGlue, aGlue);
- if (Interpret(aGlue) == 0)
- diag(kFatal, "Bad formatting string %s\n", aGlue);
- }
-
-
-
- /*
- ** Display the token. Remember what has been displayed and insert spaces as
- ** required
- */
- //µ Formatting::Display
- #pragma segment Formatting
- void Formatting::Display(Syntactic *aToken)
- {
- Boolean tokenWritten = false;
-
- if (aToken == 0)
- return;
-
- if (gDebug)
- diag(kDebug, " # Display(%s)\n", Parser::NameOf(aToken->Type()));
-
- switch (aToken->Type()) {
- case kSLex_NewLine:
- case kSPrs_NewLine:
- {
- Boolean emitNewLine = false;
-
- // If the last newline emitted was a source newline and the current
- // output line is empty, then this newline will be emitted. This
- // lets us preserve line spacing that the user has provided.
- if (gLastNewLineFromSource && CurrentColumn() == 0)
- emitNewLine = true;
-
- // If we always pass source newlines, then pass this one along also
- if (gPassSourceNewLines)
- emitNewLine = true;
-
- // If a newline is needed, then this newline fits the bill so it
- // should be emitted.
- if (NewLineNeeded())
- emitNewLine = true;
-
- // If the last item emitted was a comment, then this newline should
- // be emitted as it is most likely a newline at the end of a comment
- if (gV.fLastTokenType == kSLex_Comment)
- emitNewLine = true;
-
- // Check if this is the second in a series of newlines. If it is, then
- // we definitely want to emit this and the previous newline. Otherwise,
- // remember that this newline is pending if this newline is not emitted
- // this time around
- if (gSourceNewLines > 0) {
- tokenWritten = aToken->Display(this);
- emitNewLine = true;
- } else if (!emitNewLine && gPassConsecutiveNewLines)
- gSourceNewLines = 1;
-
- if (emitNewLine) {
- tokenWritten = aToken->Display(this);
- gLastNewLineFromSource = gFromSource;
- gSourceNewLines = 0;
- } else if (gFromSource) {
- // If the newline is not being used, remember that it was here in
- // case we have to reformat the line
- gFormatLog->Record(Formatting::UseSourceNewLine);
- }
-
- // Remember that this token was written
- if (tokenWritten)
- gV.fLastTokenType = aToken->Type();
- }
- return;
-
- case kSLex_Comment:
- case kSLex_PoundLine:
- // Comments are special. They are allowed to ignore the gV.fWantNewLine
- // and gV.fWantSpace flags. However, gV.fNeedNewLine must be respected, along
- // with emitting any pending newlines.
- if (gSourceNewLines > 0) {
- NewLine();
- gSourceNewLines = 0;
- }
- while (NewLineNeeded())
- NewLine();
-
- tokenWritten = aToken->Display(this);
-
- // Handle a preprocessor line. The MinorType() of the token describes
- // which type of preprocessor line it is.
- if (aToken->Type() == kSLex_PoundLine)
- if (aToken->MinorType() == kSLex_PoundEndIf)
- NeedNewLine(2);
- else
- NeedNewLine(1);
-
- // Remember the type of this token if it was written
- if (tokenWritten) {
- gV.fLastTokenType = aToken->Type();
- gFormatLog->Record(Formatting::Display, aToken);
- }
- return;
-
- case kSLex_EOF:
- // Emit any deferred newlines, both those from the source and those
- // that were requested but not emitted
- if (gSourceNewLines > 0) {
- NewLine();
- gSourceNewLines = 0;
- }
- while (NewLineWanted() || NewLineNeeded())
- NewLine();
- return;
- }
-
-
- /*
- ** Make sure required newlines and spaces have been emitted. Indent to
- ** the requested column. Display the token. Reset the requested indent.
- ** Update gV.fLastTokenType. Then if the glue was expecting a text string,
- ** interpret the remainder of the glue, advancing it. Note that this might
- ** modify gV.fLastTokenType if the glue contains a "!n" or "!s" within it.
- ** gSourceNewLines is set to 0 to indicate that any pending newlines were
- ** not emitted and have been ignored.
- */
- CheckWanted();
- IndentTo(gV.fIndent);
- if (gV.fCurDeclStart < 0)
- gV.fCurDeclStart = gV.fIndent;
- tokenWritten = aToken->Display(this);
-
- // Record this transaction in the FormatLog. Also remember the
- // last token type for future adjacency checks
- if (tokenWritten) {
- gFormatLog->Record(Formatting::Display, aToken);
-
- gV.fIndent = GetIndent();
- gSourceNewLines = 0;
-
- gV.fLastTokenType = aToken->Type();
- if (gV.fLastTokenType == kSLex_Op) {
- gV.fLastTokenType = aToken->MinorType();
- if (gV.fLastTokenType == 0)
- gV.fLastTokenType = kSLex_Op;
- }
-
- if (gV.fGlueString && *gV.fGlueString == (unsigned char)'•') {
- gV.fGlueString = Interpret(++gV.fGlueString);
- if (gV.fGlueString == 0)
- diag(kFatal, "Bad formatting string %s\n", gV.fFormatString);
- }
- }
- }
-
-
-
- //µ Formatting::DeclPadStart
- #pragma segment Formatting
- void Formatting::DeclPadStart(int nOperators)
- {
- // If declaration operators go left, then there are no pad characters
- if (DeclLeft())
- return;
-
- // Want at least one space separating the operators from the type
- WantSpace();
-
- // Compute the number of pad characters required.
- if (WhatToSqueeze() != kSqueezeBlanks) {
- short nPadChars = CurrentColumn() - gV.fCurDeclStart + nOperators;
- while (nPadChars++ < fContext.fDeclWidth)
- WantSpace();
- }
- }
-
-
-
- //µ Formatting::DeclPadEnd
- #pragma segment Formatting
- void Formatting::DeclPadEnd()
- {
- // If declaration operators go right, then there are no pad characters
- if (!DeclLeft())
- return;
-
- // Want at least one space separating the operators from the type
- WantSpace();
-
- // Compute the number of pad characters required.
- if (WhatToSqueeze() != kSqueezeBlanks) {
- short nPadChars = CurrentColumn() - gV.fCurDeclStart;
- while (nPadChars++ < fContext.fDeclWidth)
- WantSpace();
- }
- }
-
-
-
- //µ Formatting::DeclWidth
- #pragma segment Formatting
- void Formatting::DeclWidth(Boolean goLeft, int aWidth)
- {
- fContext.fDeclLeft = goLeft;
- fContext.fDeclWidth = aWidth;
- gV.fCurDeclStart = -1;
- }
-
-
-
-
- /*
- ** Low level display routines
- */
-
- //µ Formatting::Print
- #pragma segment Formatting
- void Formatting::Print(const char *aString)
- {
- if (aString)
- Write(aString, strlen(aString));
- }
-
-
-
- //µ Formatting::Puts
- #pragma segment Formatting
- void Formatting::Puts(const char *aString)
- {
- gOutput->Puts(aString);
- LogicalNewLine();
- }
-
-
- //µ Formatting::Putc
- #pragma segment Formatting
- void Formatting::Putc(int aChar)
- {
- if (aChar == '\n')
- NewLine();
- else {
- _Putc(aChar);
- if (isspace(aChar))
- LogicalSpace();
- }
- }
-
-
- //µ Formatting::NewLine
- #pragma segment Formatting
- void Formatting::NewLine()
- {
- // Check if the LineLength() has been exceeded. If it has, initiate
- // the line shortening procedures. (This is a test, it is only a test)
-
- if (gDebug)
- diag(kDebug, " # \\n\n");
-
- // Emit the newline to standard out. Note that a newline has been emitted.
- // Indicate that the last newline was not from source. The line has been
- // emitted, so reset
- gOutput->Putc('\n');
- LogicalNewLine();
- gLastNewLineFromSource = false;
-
- // Checkpoint the world if required. Flush the buffer if the newline came
- // from source. We don't flush when not coming from source because the
- // newline *might* be provisional and undone when retrying.
- if (gFromSource) {
- gOutput->Flush(4096);
- gFormatLog->Checkpoint(&fContext, &fSavedContexts, fSavedContexts.GetIndex(sizeof(fContext)));
- gC = gV;
- gOutputOffset = gOutput->GetCursor();
- }
- }
-
-
- //µ Formatting::Write
- #pragma segment Formatting
- void Formatting::Write(const void *aString, size_t n)
- {
- gOutput->Write(aString, n);
-
- const char *p = (const char *)aString;
-
- while (n--) {
- char aChar = *p++;
-
- if (aChar == '\n')
- LogicalNewLine();
- else if (isspace(aChar))
- LogicalSpace();
- else
- CurrentColumn()++;
- }
- }
-
-
-
- //µ Formatting::LogicalSpace
- #pragma segment Formatting
- void Formatting::LogicalSpace()
- {
- if (SpaceWanted())
- --gV.fWantSpace;
- if (SpaceNeeded())
- --gV.fNeedSpace;
- gV.fLastTokenType = kSLex_Null;
- }
-
-
-
- //µ Formatting::LogicalNewLine
- #pragma segment Formatting
- void Formatting::LogicalNewLine()
- {
- CurrentColumn() = 0;
- if (NewLineWanted())
- --gV.fWantNewLine;
- if (NewLineNeeded())
- --gV.fNeedNewLine;
- gV.fWantSpace = 0;
- gV.fNeedSpace = 0;
- AtBOL();
- }
-
-
-
- //µ Formatting::OpenContext
- #pragma segment Formatting
- void Formatting::OpenContext(Boolean isGroup)
- {
- int aDepth = fSavedContexts.GetIndex(sizeof(fContext));
-
- gFormatLog->RecordDepth(aDepth);
- gFormatLog->Record(Formatting::OpenContext, isGroup);
-
- fSavedContexts.Write(&fContext, sizeof(fContext));
- fContext.fIsGroup = isGroup;
- if (gDebug)
- diag(kDebug, " # OpenContext(%d), SP = %d\n", isGroup, aDepth);
- }
-
-
-
- //µ Formatting::CloseContext
- #pragma segment Formatting
- void Formatting::CloseContext()
- {
- int aDepth = fSavedContexts.GetIndex(sizeof(fContext)) - 1;
-
- gFormatLog->RecordDepth(aDepth);
- gFormatLog->Record(Formatting::CloseContext);
-
- // Restore the previous context. Preserve the current column as that does
- // is a dynamic value.
- short aColumn = CurrentColumn();
-
- fSavedContexts.DecrCursor(sizeof(fContext));
- fContext = *(FContext *)fSavedContexts.GetData();
-
- CurrentColumn() = aColumn;
- gV.fIndent = GetIndent();
- if (gDebug)
- diag(kDebug, " # CloseContext, SP = %d\n", aDepth);
- }
-
-
-
- //µ Formatting::RestoreIndent
- #pragma segment Formatting
- void Formatting::RestoreIndent()
- {
- gFormatLog->Record(Formatting::RestoreIndent);
-
- FContext * aContext = (FContext *)fSavedContexts.GetData(fSavedContexts.GetCursor() - sizeof(fContext));
-
- fContext.fReg[kRegIndentDelta] = aContext->fReg[kRegIndentDelta];
- fContext.fReg[kRegWidth] = aContext->fReg[kRegWidth];
- fContext.fReg[kRegIndent] = aContext->fReg[kRegIndent];
- fContext.fReg[kRegTempIndent] = aContext->fReg[kRegTempIndent];
-
- gV.fIndent = GetIndent();
- }
-
-
-
- /*
- ** Postcondition: LogicalSpace() || LogicalNewLine()
- */
- //µ Formatting::CheckWanted
- #pragma segment Formatting
- void Formatting::CheckWanted()
- {
- // Do newlines first. This can turn off the need for spaces
- while (NewLineWanted() || NewLineNeeded())
- NewLine();
-
- // Emit spaces after newlines.
- while (SpaceWanted() || SpaceNeeded()) {
- _Putc(' ');
- LogicalSpace();
- }
-
- // Finally check if we need a newline.
- if (CurrentColumn() >= LineLength()) {
- // If we are in the middle of a Rollback(), then don't Rollback() again.
- if (gFromSource) {
- // Indicate that text is no longer from source.
- gFromSource = false;
-
- // Iterate over the squeeze options that have not been applied
- // until the current column is less than the line length at the
- // end of the squeeze. Note that the squeeze option is propagated
- // up the context stack
- short sqType = WhatToSqueeze();
-
- // Squeeze from the first choice to the last
- if (sqType < kSqueezeFirst)
- sqType = kSqueezeFirst;
-
- while (sqType < kSqueezeEnd && CurrentColumn() > LineLength()) {
- if (gDebug)
- diag(kDebug, " #\n # ——> Rollback starting. SetSqueeze(%d)\n", sqType);
-
- // Rollback the log, the global variables, and the beginning of
- // the line
- gFormatLog->Rollback(&fContext, &fSavedContexts);
- gV = gC;
- gOutput->SetCursor(gOutputOffset);
-
- // Start squeezing.
- SetSqueeze(sqType);
-
- // Redo all the saved items
- while (gFormatLog->Redo(this))
- ;
-
- if (gDebug)
- diag(kDebug, " # <—— Rollback completed\n #\n");
-
- sqType++;
- }
-
- // Done redoing. Allow redo to work.
- SetSqueeze(kSqueezeNone);
- gFormatLog->EnableRecord();
- gFromSource = true;
-
- // If still beyond the end of the line, just put out a newline
- if (CurrentColumn() >= LineLength())
- NewLine();
- }
- }
- }
-
-
- //µ Formatting::SetSqueeze
- #pragma segment Formatting
- void Formatting::SetSqueeze(short whatToSqueeze)
- {
- int minDepth = gFormatLog->MinDepth();
- int maxDepth = gFormatLog->MaxDepth();
-
- SetWhatToSqueeze(whatToSqueeze);
- while (minDepth <= maxDepth) {
- ((FContext *)fSavedContexts.GetData(minDepth, sizeof(fContext)))->fWhatToSqueeze = whatToSqueeze;
- ++minDepth;
- }
-
- gConditionalNeedNewLine = (whatToSqueeze == kSqueezeConditionalNewLine);
- gConditionalNewLineGlue = 0;
- }
-
-
- //µ Formatting::UseSourceNewLine
- #pragma segment Formatting
- void Formatting::UseSourceNewLine()
- {
- if (WhatToSqueeze() == kSqueezeUseSourceNewLines)
- NeedNewLine();
- }
-
-
-
- /*µ - FormatString interpreters
- */
-
-
- /*µ consumeWhiteSpace
- ** Return a pointer to the first non-whitespace character in the FormatString
- */
- static FormatString consumeWhiteSpace(register FormatString aFormat)
- {
- if (aFormat)
- while (isspace(*aFormat))
- ++aFormat;
-
- return (aFormat);
- }
-
-
- /*µ consumeBalancedParens
- ** Assure aFormat points at an open parenthesis, then advance it past
- ** all balanced pairs of parentheses. Return 0 if an error occurred,
- ** otherwise return the pointer to the first character past the closing
- ** parenthesis.
- */
- static FormatString consumeBalancedParens(register FormatString aFormat)
- {
- aFormat = consumeWhiteSpace(aFormat);
-
- if (aFormat && *aFormat == '{') {
- int parenCount = 0;
-
- while (*aFormat) {
- switch (*aFormat++) {
- case '{':
- ++parenCount;
- break;
-
- case '}':
- if (--parenCount == 0)
- return (consumeWhiteSpace(aFormat));
- break;
-
- case '\'':
- aFormat++;
- break;
- }
- }
- }
-
- return (0);
- }
-
-
-
- //µ Formatting::Interpret
- #pragma segment Formatting
- FormatString Formatting::Interpret(register FormatString aFormat)
- {
- short n;
- Boolean aBool;
-
- while (aFormat && *aFormat) {
- switch (*aFormat++) {
- case 'i': // Set the indent
- SetRegister(kRegIndent, aFormat);
- gV.fIndent = GetIndent();
- SetRegister(kRegTempIndent, gV.fIndent);
- if (gDebug)
- diag(kDebug, " # i%d\n", GetIndent());
- break;
-
- case 't': // Set the temporary indent
- SetRegister(kRegTempIndent, aFormat);
- if (gDebug)
- diag(kDebug, " # t%d\n", GetRegister(kRegTempIndent));
- break;
-
- case 'c': // Set the next column
- gV.fIndent = GetExpr(aFormat);
- if (gDebug)
- diag(kDebug, " # c%d\n", gV.fIndent);
- break;
-
- case '/': // Conditional break
- {
- Boolean didNewLine = false;
-
- if (*aFormat == '+')
- SetRegister(kRegTempIndent, aFormat);
- if (gConditionalNeedNewLine && (gConditionalNewLineGlue == 0 || gConditionalNewLineGlue == aFormat)) {
- didNewLine = true;
- gConditionalNewLineGlue = aFormat;
- NeedNewLine();
- }
- if (gDebug)
- diag(kDebug, " # / (%d)\n", didNewLine);
- }
- break;
-
- case 'd': // Declaration width
- aBool = (*aFormat++ == 'l');
- n = GetExpr(aFormat);
- DeclWidth(aBool, n);
- break;
-
- case 's': // Emit blanks
- aBool = (*aFormat == '#');
- if (aBool)
- ++aFormat;
- n = GetExpr(aFormat);
- if (aBool) {
- if (WhatToSqueeze() != kSqueezeBlanks)
- WantSpace(n);
- } else
- NeedSpace(n);
- if (gDebug)
- diag(kDebug, " # s%s%d\n", (aBool ? "#" : ""), n);
- break;
-
- case 'n': // Want new lines
- aBool = (*aFormat == '#');
- if (aBool)
- ++aFormat;
- n = GetExpr(aFormat);
- if (aBool)
- WantNewLine(n);
- else
- NeedNewLine(n);
- if (gDebug)
- diag(kDebug, " # n%s%d\n", (aBool ? "#" : ""), n);
- break;
-
- case '&': // Require
- switch (*aFormat++) {
- case 'n': // …beginning of line
- aBool = (!IsBOL() && !NewLineWanted() && !NewLineNeeded());
- if (aBool)
- NeedNewLine();
- if (gDebug)
- diag(kDebug, " # &n(%d)\n", aBool);
- break;
-
- case 's': // …last was space
- aBool = (!IsBOL() && gV.fLastTokenType != kSLex_Null && !SpaceWanted() && !SpaceNeeded());
- if (aBool)
- NeedSpace();
- if (gDebug)
- diag(kDebug, " # &s(%d)\n", aBool);
- break;
-
- default:
- return (0);
- }
- break;
-
- case '!': // Assert…
- switch (*aFormat++) {
- case 'n': // …beginning of line
- if (!NewLineWanted() && !NewLineNeeded())
- AtBOL();
- else {
- if (NewLineWanted())
- gV.fWantNewLine--;
- if (NewLineNeeded())
- gV.fNeedNewLine--;
- }
-
- if (gDebug)
- diag(kDebug, " # !n\n");
- break;
-
- case 's': // …last was space
- if (!SpaceWanted() && !SpaceNeeded())
- gV.fLastTokenType = kSLex_Null;
- else {
- if (SpaceWanted())
- gV.fWantSpace--;
- if (SpaceNeeded())
- gV.fNeedSpace--;
- }
-
- if (gDebug)
- diag(kDebug, " # !s\n");
- break;
-
- default:
- return (0);
- }
- break;
-
- case '?': // Conditional…
- switch (*aFormat++) {
- case 'n': // …beginning of line
- aBool = IsBOL();
- break;
-
- case 'i': // …last was id or value
- aBool = IsIdentifierType(gV.fLastTokenType);
- break;
-
- case 'o': // …last was operator
- aBool = IsOperatorType(gV.fLastTokenType);
- break;
-
- case '\'':
- switch (*aFormat++) {
- case '(': // …last was left paren
- aBool = (gV.fLastTokenType == kSLex_LParen);
- break;
-
- case ')': // …last was right paren
- aBool = (gV.fLastTokenType == kSLex_RParen);
- break;
-
- case '{': // …last was left curly
- aBool = (gV.fLastTokenType == kSLex_LCurly);
- break;
-
- case '}': // …last was right curly
- aBool = (gV.fLastTokenType == kSLex_RCurly);
- break;
-
- case ';': // …last was operator
- aBool = (gV.fLastTokenType == kSLex_SemiColon);
- break;
-
- default: // …error
- return (0);
- }
- break;
-
- default: // …error
- return (0);
- }
-
- if (gDebug)
- diag(kDebug, " # ?%c == %d\n", aFormat[-1], aBool);
-
- if (!aBool)
- aFormat = consumeBalancedParens(aFormat);
- if (aFormat == 0 || *aFormat++ != '{')
- return (0);
- break;
-
- case '=': // Assign to a register
- if (!isdigit(*aFormat))
- return (0);
- n = *aFormat++ - '0';
- SetRegister(n, aFormat);
- if (gDebug)
- diag(kDebug, " # =%d %d\n", n, GetRegister(n));
- break;
-
- case ((unsigned char)'•'): // Display an item
- if (gDebug)
- diag(kDebug, " # •\n");
- return (aFormat - 1);
-
- case ' ': // Ignore whitespace
- case '\t':
- break;
-
- case '{': // Ignore it
- aFormat = consumeBalancedParens(aFormat - 1);
- break;
-
- case '}': // Skip over it
- break;
-
- default: // Unknown character.
- return (aFormat - 1);
- }
- }
-
- return (aFormat);
- }
-
-
-
- /*
- ** Set the register to the value of the expression in the string
- */
- //µ Formatting::SetRegister
- #pragma segment Formatting
- void Formatting::SetRegister(int aRegister, FormatString &aFormat)
- {
- int op;
-
- switch (*aFormat) {
- case '+':
- case '-':
- op = *aFormat++;
- break;
- default:
- op = '=';
- break;
- }
-
- int expr = GetExpr(aFormat);
- if (aFormat != 0) {
- switch (op) {
- case '+':
- SetRegister(aRegister, GetRegister(aRegister) + expr);
- break;
-
- case '-':
- SetRegister(aRegister, GetRegister(aRegister) - expr);
- break;
-
- case '=':
- SetRegister(aRegister, expr);
- break;
- }
- }
- }
-
-
-
- /*
- ** Evaluate the expression. Return the value and advance aFormat past the
- ** expression. If there was an error, aFormat == 0. If there was no
- ** expression, return the default value.
- */
- //µ Formatting::GetExpr
- #pragma segment Formatting
- int Formatting::GetExpr(FormatString &aFormat, int defaultValue)
- {
- int sum = defaultValue;
- int op = '=';
- Boolean done = false;
-
- while (!done && aFormat && *aFormat) {
- int term;
- Boolean haveTerm = false;
-
- switch (*aFormat) {
- case '+': // Add to sum
- case '-': // Subtract from sum
- // Remember operation, get operand.
- op = *aFormat++;
- break;
-
- case ((unsigned char)'®'): // Register reference
- if (!isdigit(*++aFormat)) {
- aFormat = 0;
- return (0);
- }
- term = GetRegister(*aFormat++ - '0');
- haveTerm = true;
- break;
-
- case ' ': // Ignore whitespace
- case '\t':
- ++aFormat;
- break;
-
- default: // Number
- if (isdigit(*aFormat)) {
- term = 0;
- while (isdigit(*aFormat)) {
- term *= 10;
- term += *aFormat++ - '0';
- }
- haveTerm = true;
- } else
- done = true;
- break;
- }
-
- // Here after having scanned a term. op is the operation.
- if (haveTerm)
- switch (op) {
- case '=':
- sum = term;
- break;
- case '+':
- sum += term;
- break;
- case '-':
- sum -= term;
- break;
- }
- }
-
- return (sum);
- }
-
-
-
- /*
- ** Return true if the last token was a beginning of line token type
- */
- //µ Formatting::IsBOL
- #pragma segment Formatting
- Boolean Formatting::IsBOL()
- {
- return (gV.fLastTokenType == kSLex_NewLine || gV.fLastTokenType == kSPrs_NewLine);
- }
-
-
-
- /*
- ** Return true if the type is one which should be treated as an identifier
- ** for formatting purposes. This class subsumes reserved words and values
- ** in addition to identifiers as these may not be placed adjacent to each
- ** other with impunity
- */
- //µ Formatting::IsIdentifierType
- #pragma segment Formatting
- Boolean Formatting::IsIdentifierType(short aType)
- {
- switch (aType) {
- case kSLex_Id:
- case kSLex_ParsedId:
- case kSLex_Value:
- case kSPrs_Id:
- case kSPrs_DeclOperator:
- return (true);
-
- default:
- return (aType >= kSLex_ReservedWordFirst && aType <= kSLex_ReservedWordLast);
- }
- }
-
-
-
- /*
- ** Return true if the type is that belonging to an operator
- */
- //µ Formatting::IsOperatorType
- #pragma segment Formatting
- Boolean Formatting::IsOperatorType(short aType)
- {
- return (aType >= kSLex_OpFirst && aType <= kSLex_OpLast);
- }
-
-
-